package org.black.phoneUtility.util.database.backupRestore;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import org.black.phoneUtility.R;
import org.black.phoneUtility.app.service.StartNeededService;
import org.black.phoneUtility.database.sqlite.PhoneUtilitySQLiteOpenHelper;
import org.black.phoneUtility.sms.reply.provider.SmsReplyContract;
import org.black.phoneUtility.sms.schedule.provider.SmsScheduleContract;
import org.black.phoneUtility.util.database.backupRestore.exception.DatabaseVersionDoesNotMatchException;

import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.media.MediaScannerConnection;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;

public final class BackupRestoreUtility {
	private static final String LOG_TAG = BackupRestoreUtility.class.getName();

	private BackupRestoreUtility() {
	}

	public static void backupDatabase(Context context, File sourceFile,
			File destinationFile, boolean backupGlobaltSettings,
			boolean backupSmsReply, boolean backupSmsSchedule)
			throws IOException {
		copyFile(sourceFile, destinationFile);
		MediaScannerConnection.scanFile(context,
				new String[] { sourceFile.getAbsolutePath() }, null, null);

		SQLiteDatabase destinationSqLiteDatabase = SQLiteDatabase.openDatabase(
				destinationFile.getAbsolutePath(), null,
				SQLiteDatabase.OPEN_READWRITE, null);
		destinationSqLiteDatabase.disableWriteAheadLogging();
		destinationSqLiteDatabase.setForeignKeyConstraintsEnabled(true);
		destinationSqLiteDatabase.beginTransaction();
		if (backupGlobaltSettings == false) {
			deleteEnvironmentSetting(destinationSqLiteDatabase);
		}
		if (backupSmsReply == false) {
			deleteSmsReply(destinationSqLiteDatabase);
		}
		if (backupSmsSchedule == false) {
			deleteSmsSchedule(destinationSqLiteDatabase);
		}
		destinationSqLiteDatabase.setTransactionSuccessful();
		destinationSqLiteDatabase.endTransaction();
		destinationSqLiteDatabase.close();
	}

	public static void restoreDatabase(Context context, File sourceFile,
			File destinationFile, boolean backupEnvironmentSetting,
			boolean backupSmsReply, boolean backupSmsSchedule)
			throws IOException, DatabaseVersionDoesNotMatchException,
			SQLiteException {
		try {
			PhoneUtilitySQLiteOpenHelper phoneUtilitySQLiteOpenHelper = new PhoneUtilitySQLiteOpenHelper(
					context);
			phoneUtilitySQLiteOpenHelper.close();
		} catch (Throwable throwable) {

		}
		preCheckDatabaseVersion(sourceFile, destinationFile);
		backupDatabase(context, sourceFile, destinationFile,
				backupEnvironmentSetting, backupSmsReply, backupSmsSchedule);
		postCheckDatabaseVersion(context, sourceFile, destinationFile);

		// Restore SMS Reply Preference
		{
			SQLiteDatabase currentSqLiteDatabase = null;
			Cursor smsReplyGlobalSettingCursor = null;
			try {
				currentSqLiteDatabase = SQLiteDatabase.openDatabase(
						destinationFile.getAbsolutePath(), null,
						SQLiteDatabase.OPEN_READWRITE, null);
				smsReplyGlobalSettingCursor = currentSqLiteDatabase
						.query(PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.GLOBAL_SETTINGS,
								null,
								SmsReplyContract.SmsReplyGlobalSettings._ID
										+ " = ? ",
								new String[] { String.valueOf(1) }, null, null,
								null);
				if (smsReplyGlobalSettingCursor != null
						&& smsReplyGlobalSettingCursor.moveToFirst() == true) {
					SharedPreferences sharedPreferences = PreferenceManager
							.getDefaultSharedPreferences(context);
					Editor editor = sharedPreferences.edit();
					editor.putBoolean(
							context.getString(R.string.preference_sms_reply_enable_avoid_excessive_reply),
							1 == smsReplyGlobalSettingCursor.getInt(smsReplyGlobalSettingCursor
									.getColumnIndex(SmsReplyContract.SmsReplyGlobalSettings.ENABLE_AVOID_EXCESSIVE_REPLY)) ? true
									: false);
					editor.putBoolean(
							context.getString(R.string.preference_sms_reply_show_notification),
							1 == smsReplyGlobalSettingCursor.getInt(smsReplyGlobalSettingCursor
									.getColumnIndex(SmsReplyContract.SmsReplyGlobalSettings.SHOULD_NOTIFICATION_DISPLAY_IN_NOTIFICATION_BAR)) ? true
									: false);
					editor.putBoolean(
							context.getString(R.string.preference_sms_reply_do_not_send_reply_message_on_roaming),
							1 == smsReplyGlobalSettingCursor.getInt(smsReplyGlobalSettingCursor
									.getColumnIndex(SmsReplyContract.SmsReplyGlobalSettings.DO_NOT_SEND_REPLY_MESSAGE_ON_ROAMING)) ? true
									: false);
					editor.commit();
				}
			} finally {
				if (smsReplyGlobalSettingCursor != null) {
					try {
						smsReplyGlobalSettingCursor.close();
					} catch (Throwable t) {
						Log.e(LOG_TAG,
								"Fail to close smsReplyGlobalSettingCursor!", t);
					}
				}
				if (currentSqLiteDatabase != null) {
					try {
						currentSqLiteDatabase.close();
					} catch (Throwable t) {
						Log.e(LOG_TAG, "Fail to close currentSqLiteDatabase!",
								t);
					}
				}
			}
		}

		// Restore SMS Schedule Preference
		{
			SQLiteDatabase currentSqLiteDatabase = null;
			Cursor smsScheduleGlobalSettingCursor = null;
			try {
				currentSqLiteDatabase = SQLiteDatabase.openDatabase(
						destinationFile.getAbsolutePath(), null,
						SQLiteDatabase.OPEN_READWRITE, null);
				smsScheduleGlobalSettingCursor = currentSqLiteDatabase
						.query(PhoneUtilitySQLiteOpenHelper.Tables.SMS.Schedule.GLOBAL_SETTINGS,
								null,
								SmsScheduleContract.SmsScheduleGlobalSettings._ID
										+ " = ? ",
								new String[] { String.valueOf(1) }, null, null,
								null);
				if (smsScheduleGlobalSettingCursor != null
						&& smsScheduleGlobalSettingCursor.moveToFirst() == true) {
					SharedPreferences sharedPreferences = PreferenceManager
							.getDefaultSharedPreferences(context);
					Editor editor = sharedPreferences.edit();
					editor.putBoolean(
							context.getString(R.string.preference_sms_schedule_do_not_send_schedule_message_on_roaming),
							1 == smsScheduleGlobalSettingCursor.getInt(smsScheduleGlobalSettingCursor
									.getColumnIndex(SmsScheduleContract.SmsScheduleGlobalSettings.DO_NOT_SEND_SCHEDULE_MESSAGE_ON_ROAMING)) ? true
									: false);
					editor.commit();
				}
			} finally {
				if (smsScheduleGlobalSettingCursor != null) {
					try {
						smsScheduleGlobalSettingCursor.close();
					} catch (Throwable t) {
						Log.e(LOG_TAG,
								"Fail to close smsScheduleGlobalSettingCursor!",
								t);
					}
				}
				if (currentSqLiteDatabase != null) {
					try {
						currentSqLiteDatabase.close();
					} catch (Throwable t) {
						Log.e(LOG_TAG, "Fail to close currentSqLiteDatabase!",
								t);
					}
				}
			}
		}
		context.getContentResolver().notifyChange(
				SmsReplyContract.SmsReplies.CONTENT_URI, null);
		context.getContentResolver().notifyChange(
				SmsReplyContract.ReplyHistories.CONTENT_URI, null);

		context.getContentResolver().notifyChange(
				SmsScheduleContract.SmsSchedules.CONTENT_URI, null);
		context.getContentResolver()
				.notifyChange(
						SmsScheduleContract.SmsScheduleSendingRecordRecipients.CONTENT_URI,
						null);

		Intent startNeededServiceIntent = new Intent(context,
				StartNeededService.class);
		Bundle arguments = new Bundle();
		startNeededServiceIntent.putExtras(arguments);
		context.startService(startNeededServiceIntent);
		Log.d(LOG_TAG, "Start StartNeededService!");
	}

	private static void copyFile(File sourceFile, File destinationFile)
			throws IOException {
		FileInputStream fileInputStream = null;
		OutputStream outputStream = null;
		try {
			fileInputStream = new FileInputStream(sourceFile);
			outputStream = new FileOutputStream(destinationFile);
			byte[] buf = new byte[1024];
			int len;
			while ((len = fileInputStream.read(buf)) > 0) {
				outputStream.write(buf, 0, len);
			}
			outputStream.flush();
		} finally {
			try {
				fileInputStream.close();
			} catch (IOException e) {
				Log.e(LOG_TAG, "Faile to close File Input Stream.!", e);
			}
			try {
				outputStream.close();
			} catch (IOException e) {
				Log.e(LOG_TAG, "Faile to close File Output Stream.!", e);
			}
		}
	}

	private static void deleteEnvironmentSetting(SQLiteDatabase sqLiteDatabase) {
		sqLiteDatabase.execSQL("DELETE FROM "
				+ PhoneUtilitySQLiteOpenHelper.Tables.GLOBAL_SETTINGS);
		Log.d(LOG_TAG, "Delete all Environment Setting related tables.");
	}

	private static void deleteSmsReply(SQLiteDatabase sqLiteDatabase) {
		ContentValues globalSettingContentValues = new ContentValues();
		globalSettingContentValues
				.put(SmsReplyContract.SmsReplyGlobalSettings.ENABLE_AVOID_EXCESSIVE_REPLY,
						1);
		globalSettingContentValues
				.put(SmsReplyContract.SmsReplyGlobalSettings.SHOULD_NOTIFICATION_DISPLAY_IN_NOTIFICATION_BAR,
						1);
		sqLiteDatabase.update(
				PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.GLOBAL_SETTINGS,
				globalSettingContentValues,
				SmsReplyContract.SmsReplyGlobalSettings._ID + " = ?",
				new String[] { String.valueOf(1) });
		sqLiteDatabase.execSQL("DELETE FROM "
				+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.REPLIES);
		sqLiteDatabase
				.execSQL("DELETE FROM "
						+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.ACTIVATION_RECORDS);
		Log.d(LOG_TAG, "Delete all SMS Reply related tables.");
	}

	private static void deleteSmsSchedule(SQLiteDatabase sqLiteDatabase) {
		sqLiteDatabase.execSQL("DELETE FROM "
				+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Schedule.SCHEDULES);
		sqLiteDatabase
				.execSQL("DELETE FROM "
						+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Schedule.SENDING_RECORDS);
		sqLiteDatabase
				.execSQL("DELETE FROM "
						+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Schedule.SENDING_RECORD_RECIPIENTS);
		Log.d(LOG_TAG, "Delete all SMS Reply related tables.");
	}

	private static boolean preCheckDatabaseVersion(File sourceFile,
			File destinationFile) throws DatabaseVersionDoesNotMatchException,
			SQLiteException {
		boolean result = true;
		SQLiteDatabase sourceSqLiteDatabase = SQLiteDatabase.openDatabase(
				sourceFile.getAbsolutePath(), null,
				SQLiteDatabase.OPEN_READWRITE, null);

		SQLiteDatabase destinationSqLiteDatabase = SQLiteDatabase.openDatabase(
				destinationFile.getAbsolutePath(), null,
				SQLiteDatabase.OPEN_READWRITE, null);
		int sourceVersion = sourceSqLiteDatabase.getVersion();
		int destinationVersion = destinationSqLiteDatabase.getVersion();

		if (sourceVersion > destinationVersion) {
			String message = "sourceVersion: " + sourceVersion
					+ ", destinationVersion: " + destinationVersion;
			throw new DatabaseVersionDoesNotMatchException(message);
		}

		sourceSqLiteDatabase.close();
		destinationSqLiteDatabase.close();
		return result;

	}

	private static boolean postCheckDatabaseVersion(Context context,
			File sourceFile, File destinationFile)
			throws DatabaseVersionDoesNotMatchException, SQLiteException {
		boolean result = true;
		SQLiteDatabase sourceSqLiteDatabase = SQLiteDatabase.openDatabase(
				sourceFile.getAbsolutePath(), null,
				SQLiteDatabase.OPEN_READWRITE, null);

		SQLiteDatabase destinationSqLiteDatabase = SQLiteDatabase.openDatabase(
				destinationFile.getAbsolutePath(), null,
				SQLiteDatabase.OPEN_READWRITE, null);
		int sourceVersion = sourceSqLiteDatabase.getVersion();
		int destinationVersion = destinationSqLiteDatabase.getVersion();

		PhoneUtilitySQLiteOpenHelper phoneUtilitySQLiteOpenHelper = new PhoneUtilitySQLiteOpenHelper(
				context);
		phoneUtilitySQLiteOpenHelper.onUpgrade(destinationSqLiteDatabase,
				sourceVersion, destinationVersion);

		sourceSqLiteDatabase.close();
		destinationSqLiteDatabase.close();
		return result;

	}

}
